#pragma once

#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <sstream>
#include <unistd.h>
#include <memory>

#include "MyMutex.hpp"

namespace LogArea
{
    using namespace MyMutexArea;
    using namespace LogArea;

    const std::string defaultfilepath = "./log/";  // 默认文件路径
    const std::string defaultfilename = "log.txt"; // 默认文件名

    std::string CurrentTime() // 获得当前时间
    {
        time_t time_stamp = ::time(nullptr); // 获取当前时间的时间戳
        struct tm curr_time;
        localtime_r(&time_stamp, &curr_time);

        char buffer[256];
        snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d-%02d-%02d",
                 curr_time.tm_year + 1900,
                 curr_time.tm_mon + 1,
                 curr_time.tm_yday,
                 curr_time.tm_hour,
                 curr_time.tm_min,
                 curr_time.tm_sec); // 按照指定格式打印当前时间,写入 buffer中
        return buffer;
    }

    enum class LogLevel // 日志等级
    {
        DEBUG = 1, // 调试
        INFO,
        WARNING, // 警告
        ERROR,   // 错误
        FATALER  // 致命错误
    };

    std::string LevelToString(LogLevel &loglevel)
    {
        switch (loglevel)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATALER:
            return "FATALER";
        default:
            return "None";
        }
    }

    class LogStrategy // 日志刷新策略
    {
    public:
        virtual ~LogStrategy() = default;
        virtual void SyncLog(const std::string &message) = 0;
    };

    class ConsoleStrategy : public LogStrategy // 显示器刷新策略
    {
    public:
        ConsoleStrategy()
        {
        }

        // 重写基类虚函数
        void SyncLog(const std::string &message) // 将信息刷新到显示器上
        {
            LockGuard lockguard(_mutex);
            std::cout << message << std::endl;
        }

        ~ConsoleStrategy()
        {
        }

    private:
        MyMutex _mutex;
    };

    class FileStrategy : public LogStrategy // 文件刷新策略
    {
    public:
        FileStrategy(const std::string filepath = defaultfilepath, const std::string filename = defaultfilename)
            : _filepath(filepath), _filename(filename)
        {
            LockGuard lockguard(_mutex);

            if (std::filesystem::exists(_filepath))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_filepath);
            }
            catch (std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << "\n";
            }
        }

        // 重写基类虚函数
        void SyncLog(const std::string& message)
        {
            LockGuard lockguard(_mutex); // 加锁

            std::string file_path_name = _filepath + _filename;
            // ofstream打开一个文件, std::ios::app每次写入按照追加写入的方式
            std::ofstream out(file_path_name, std::ios::app);
            if (!out.is_open()) // 打开文件失败
            {
                return;
            }

            out << message << "\n"; // 往文件中写入数据
            out.close();
        }

        ~FileStrategy()
        {
        }

    private:
        std::string _filepath;
        std::string _filename;

        MyMutex _mutex;
    };

    class Log // 日志，打印日志信息，根据策略进行刷新
    {
    public:
        Log() // 默认采用显示器刷新策略
        {
            _strategy = std::make_shared<ConsoleStrategy>();
        }

        void UseConsoleStrategy() // 使用显示器刷新策略
        {
            _strategy = std::make_shared<ConsoleStrategy>();
        }

        void UseFileStrategy() // 使用文件刷新策略
        {
            _strategy = std::make_shared<FileStrategy>();
        }

        ~Log()
        {
        }

        class LogMessage // 负责形成日志信息
        {
        public:
            LogMessage(LogLevel loglevel, const std::string loc_filename, int line, Log &log) // 初始化日志格式
                : _curr_time(CurrentTime()), _loglevel(loglevel), _pid(::getpid()), _loc_filename(loc_filename), _line(line), _log(log)
            {
                std::stringstream ssbuffer; // 仿cout等 流的方式写入数据
                ssbuffer << "[" << _curr_time << "] "
                         << "[" << LevelToString(_loglevel) << "] "
                         << "[" << _pid << "] "
                         << '[' << _loc_filename << "] "
                         << "[" << _line << "] - ";

                _loginfo = ssbuffer.str(); //
            }

            template <class T>
            LogMessage &operator<<(const T &info) // 重载 <<, 写入日志信息时能够仿 cout 方式写入
            {
                std::stringstream ssbuffer;
                ssbuffer << info;
                _loginfo += ssbuffer.str();
                return *this;
            }

            ~LogMessage()
            {
                if (_log._strategy) // 如果有刷新策略, 则在析构(日志生成完毕)后根据刷新策略刷新日志信息
                    _log._strategy->SyncLog(_loginfo);
            }

        private:
            std::string _curr_time;    // 当前日志时间
            LogLevel _loglevel;        // 日志信息的等级
            pid_t _pid;                // 进程 id
            std::string _loc_filename; // 错误所在文件名称
            int _line;                 // 日志信息所在行号
            Log &_log;                 // 负责根据不同策略刷新
            std::string _loginfo;      // 完整的日志信息
        };

        // 这里就是要产生拷贝, 使用 << 时就可以将上一个 << 的信息传给下一个: std::cout<<"15:"<<"15";
        LogMessage operator()(LogLevel level, const std::string &loc_filename, int line)
        {
            return LogMessage(level, loc_filename, line, *this);
        }

    private:
        // 使用基类, 就可以使用两个子类的刷新策略
        std::shared_ptr<LogStrategy> _strategy; // 日志刷新的策略, 交给智能指针管理
    };

    Log log;

#define LOG(LEVEL) log(LEVEL, __FILE__, __LINE__)      // 定义用户使用日志的格式
#define USE_CONSOLE_STRATEGY() log.UseConsoleStrategy() // 定义使用显示器刷新策略的宏
#define USE_FILE_STRATEGY() log.UseFileStrategy()       // 定义使用文件刷新策略的宏
}

//生成一份冒泡排序      
               